/*
The MIT License (MIT)

Copyright (c) 2013 Mike Dapiran, Brian May, Richard Pospesel, and Bert Wierenga

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software 
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
#include "RenderingTextureBuffer.h"
#include "TinyXML.h"
#include "../Managers/DX11Manager.h"

namespace Hogshead
{
	namespace DX11Rendering
	{
		HashTable<String, DXGI_FORMAT>* RenderingTextureBuffer::_lookup = NULL;

		DXGI_FORMAT RenderingTextureBuffer::nameToValue(const String& in_name)
		{
			if( !_lookup )
			{
				_lookup = new HashTable<String, DXGI_FORMAT>();	

				_lookup->add( "DXGI_FORMAT_R32_TYPELESS", DXGI_FORMAT_R32_TYPELESS );
				_lookup->add( "DXGI_FORMAT_R32G32B32A32_FLOAT", DXGI_FORMAT_R32G32B32A32_FLOAT );
				_lookup->add( "DXGI_FORMAT_R32_FLOAT", DXGI_FORMAT_R32_FLOAT );
			}

			DXGI_FORMAT result;
			bool errorless_return = _lookup->tryGetValue(in_name, result);
			if( !errorless_return )
				error(StringBuilder() << "attempting to lookup a value not defined: " << in_name);

			return result;
		}

		RenderingTextureBuffer::RenderingTextureBuffer(void) : _srv(NULL), _rtv(NULL), _dsv(NULL),
			_current_binding((RenderingBufferUsage)0)
		{
		}

		RenderingTextureBuffer::~RenderingTextureBuffer(void)
		{
		}

		RenderingTextureBuffer::RenderingTextureBuffer( const RenderingTextureBuffer& in_other )
		{
			_srv = in_other._srv;
			_srv->AddRef();

			_rtv = in_other._rtv;
			_rtv->AddRef();

			_dsv = in_other._dsv;
			_dsv->AddRef();

			_current_binding = in_other._current_binding;
		}

		RenderingTextureBuffer& RenderingTextureBuffer::operator=( const RenderingTextureBuffer& in_other )
		{
			_srv = in_other._srv;
			_srv->AddRef();

			_rtv = in_other._rtv;
			_rtv->AddRef();

			_dsv = in_other._dsv;
			_dsv->AddRef();

			_current_binding = in_other._current_binding;

			return *this;
		}

		ID3D11RenderTargetView* RenderingTextureBuffer::renderTargetView()
		{
			return _rtv;
		}

		ID3D11DepthStencilView* RenderingTextureBuffer::depthStencilView()
		{
			return _dsv;
		}

		ID3D11ShaderResourceView* RenderingTextureBuffer::shaderResourceView()
		{
			return _srv;
		}

		
		void RenderingTextureBuffer::readXML( const String& in_xml )
		{
			//parse the String for what we need
			TiXmlDocument document;
			document.Parse(in_xml);

			TiXmlElement* root_element = document.RootElement();
			String root_tag(root_element->Value());

			if(root_tag.equals("RenderingTextureBuffer"))
			{
				TiXmlNode* child_node = NULL;
				TiXmlElement* child_element;

				//parse all the  attributes
				D3D11_TEXTURE2D_DESC text_desc;
				ZeroMemory(&text_desc, sizeof( D3D11_TEXTURE2D_DESC ) );

				//width
				root_element->QueryUnsignedIntAttribute("Width", &text_desc.Width);
				if(!text_desc.Width)
					text_desc.Width = DX11Manager::getWidth();

				//height
				root_element->QueryUnsignedIntAttribute("Height", &text_desc.Height);
				if(!text_desc.Height)
					text_desc.Height = DX11Manager::getHeight();

				//mip levels
				root_element->QueryUnsignedIntAttribute("MipLevels", &text_desc.MipLevels);

				//array size
				root_element->QueryUnsignedIntAttribute("ArraySize", &text_desc.ArraySize);
				if(!text_desc.ArraySize)
					text_desc.ArraySize = 1;

				//format
				String string_from_xml;
				root_element->QueryStringAttribute("Format", &string_from_xml);
				text_desc.Format = nameToValue( string_from_xml );

				//multisampling
				DXGI_SAMPLE_DESC sample_desc;
				root_element->QueryUnsignedIntAttribute("SampleCount", &sample_desc.Count);
				root_element->QueryUnsignedIntAttribute("SampleQuality", &sample_desc.Quality);
				text_desc.SampleDesc = sample_desc;

				//usage
				text_desc.Usage = D3D11_USAGE_DEFAULT;

				//binding flags
				//needs to be read later after we parse child elements

				//cpu access
				String cpu_read_write_access;
				root_element->QueryStringAttribute("CPUAccessFlags", &cpu_read_write_access);

				if( cpu_read_write_access.equals("r") ||
					cpu_read_write_access.equals("rw") ||
					cpu_read_write_access.equals("wr") )
					text_desc.CPUAccessFlags |= D3D11_CPU_ACCESS_READ;

				if( cpu_read_write_access.equals("w") ||
					cpu_read_write_access.equals("rw") ||
					cpu_read_write_access.equals("wr")  )
					text_desc.CPUAccessFlags |= D3D11_CPU_ACCESS_WRITE;

				//misc flags
				root_element->QueryUnsignedIntAttribute("MiscFlags", &text_desc.MiscFlags);

				//parse the child elements
				bool is_rtv = false;
				bool is_srv = false;
				bool is_dsv = false;
				
				D3D11_RENDER_TARGET_VIEW_DESC rtv_desc;
				D3D11_RENDER_TARGET_VIEW_DESC* rtv_desc_p = NULL;

				D3D11_DEPTH_STENCIL_VIEW_DESC dsv_desc;
				D3D11_DEPTH_STENCIL_VIEW_DESC* dsv_desc_p = NULL;

				D3D11_SHADER_RESOURCE_VIEW_DESC srv_desc;
				D3D11_SHADER_RESOURCE_VIEW_DESC* srv_desc_p = NULL;

				while(child_node = root_element->IterateChildren(child_node))
				{
					child_element = child_node->ToElement();
					if(!child_element)
						continue;

					String tag(child_element->Value());
					if(tag.equals("RenderTargetView"))
					{
						if(is_rtv)
							error("A texture can only be one RenderTargetView");

						is_rtv = true;
						rtv_desc_p = &rtv_desc;
						text_desc.BindFlags |= D3D11_BIND_RENDER_TARGET;

						//parse out all description info						
						ZeroMemory(&rtv_desc, sizeof( rtv_desc));

						//format
						rtv_desc.Format = text_desc.Format;
						rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
						rtv_desc.Texture2D.MipSlice = 0;
					}
					else if(tag.equals("ShaderResourceView"))
					{
						if(is_srv)
							error("A texture can only be one ShaderResourceView");

						is_srv = true;
						srv_desc_p = &srv_desc;
						text_desc.BindFlags |= D3D11_BIND_SHADER_RESOURCE;

						//parse out all description info						
						ZeroMemory(&srv_desc, sizeof( srv_desc ));
						
						srv_desc.Format = text_desc.Format;
						srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
						srv_desc.Texture2D.MipLevels = 1;
						srv_desc.Texture2D.MostDetailedMip = 0;
					}
					else if(tag.equals("DepthStencilView"))
					{
						if(is_dsv)
							error("A texture can only be one DepthStencilView");

						is_dsv = true;
						dsv_desc_p = &dsv_desc;
						text_desc.BindFlags |= D3D11_BIND_DEPTH_STENCIL;

						//parse out all description info
						error("need to implement configuring DSV");
					}
					
				}

				build( text_desc, rtv_desc_p, dsv_desc_p, srv_desc_p );
			}
			
		}

		void RenderingTextureBuffer::build(const D3D11_TEXTURE2D_DESC& in_texture2D_desc, const D3D11_RENDER_TARGET_VIEW_DESC* in_rtv_desc,
					 const D3D11_DEPTH_STENCIL_VIEW_DESC* in_dsv_desc, const D3D11_SHADER_RESOURCE_VIEW_DESC* in_srv_desc, const char* in_opt_name, int in_opt_size )
		{
			ID3D11Texture2D* texture2d;

			//create the texture
			DX11Manager::createTexture2D( &in_texture2D_desc, NULL, &texture2d, in_opt_name, in_opt_size );

			

			//create any views requested
			if( in_rtv_desc )
				DX11Manager::createRenderTargetView( texture2d, in_rtv_desc, &_rtv );

			if( in_dsv_desc )
				DX11Manager::createDepthStencilView(texture2d, in_dsv_desc, &_dsv  );

			if( in_srv_desc )
				DX11Manager::createShaderResourceView( texture2d, in_srv_desc, &_srv );

			texture2d->Release();
		}

		void RenderingTextureBuffer::finalize()
		{
			if( _srv )
			{
				_srv->Release();
				_srv = NULL;
			}
			if( _rtv )
			{
				_rtv->Release();
				_rtv = NULL;
			}
			if( _dsv )
			{
				_dsv->Release();
				_dsv = NULL;
			}

			_current_binding = (RenderingBufferUsage)0;
		}
	}
}